泛型

1. 概述

泛型(Generics)是C#中一个强大的特性,允许你编写可以处理多种数据类型的代码,而不需要为每种数据类型编写单独的实现。泛型可以提高代码的重用性、类型安全性和性能。

1.1 为什么需要泛型?

在泛型出现之前,开发者通常使用object类型来处理不同类型的数据。然而,这种方式存在以下问题:

  • 类型安全问题:使用object类型时,编译器无法在编译时检查类型,容易导致运行时错误。
  • 性能问题:将值类型转换为object类型(装箱)和从object类型转换回值类型(拆箱)会导致性能损失。

泛型的引入解决了这些问题,使得代码更加安全、高效。

2. 泛型类

泛型类是使用泛型参数定义的类。泛型参数用尖括号<>括起来,通常用单个大写字母表示,如T。

2.1 定义泛型类

public class GenericClass<T>
{
    private T _value;

    public GenericClass(T value)
    {
        _value = value;
    }

    public T GetValue()
    {
        return _value;
    }

    public void SetValue(T value)
    {
        _value = value;
    }
}

2.2 使用泛型类

class Program
{
    static void Main()
    {
        // 使用泛型类存储整数
        GenericClass<int> intContainer = new GenericClass<int>(10);
        Console.WriteLine(intContainer.GetValue()); // 输出: 10

        // 使用泛型类存储字符串
        GenericClass<string> stringContainer = new GenericClass<string>("Hello");
        Console.WriteLine(stringContainer.GetValue()); // 输出: Hello
    }
}

2.3 泛型类的优势

  • 类型安全:编译器会在编译时检查类型,确保类型一致性。
  • 代码重用:相同的逻辑可以应用于不同的数据类型。

3. 泛型方法

泛型方法是在方法级别使用泛型参数。泛型方法可以在非泛型类中定义。

3.1 定义泛型方法

public class GenericMethodExample
{
    public static void Swap<T>(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
}

3.2 使用泛型方法

class Program
{
    static void Main()
    {
        int x = 5, y = 10;
        GenericMethodExample.Swap(ref x, ref y);
        Console.WriteLine($"x: {x}, y: {y}"); // 输出: x: 10, y: 5

        string s1 = "Hello", s2 = "World";
        GenericMethodExample.Swap(ref s1, ref s2);
        Console.WriteLine($"s1: {s1}, s2: {s2}"); // 输出: s1: World, s2: Hello
    }
}

3.3 泛型方法的优势

  • 灵活性:可以在不改变方法签名的情况下处理不同类型的数据。
  • 类型安全:编译器会在编译时检查类型,确保类型一致性。

4. 泛型约束

泛型约束(Generic Constraints)用于限制泛型参数的类型。通过约束,可以确保泛型参数满足特定的条件。

4.1 常见的泛型约束

  • where T : class:T必须是引用类型。
  • where T : struct:T必须是值类型。
  • where T : new():T必须有一个无参构造函数。
  • where T : BaseClass:T必须是BaseClass或其子类。
  • where T : IInterface:T必须实现IInterface接口。

4.2 示例:使用泛型约束

public class GenericConstraintExample<T> where T : new()
{
    public T CreateInstance()
    {
        return new T();
    }
}

class Program
{
    static void Main()
    {
        GenericConstraintExample<MyClass> example = new GenericConstraintExample<MyClass>();
        MyClass instance = example.CreateInstance();
        Console.WriteLine(instance.GetType().Name); // 输出: MyClass
    }
}

public class MyClass
{
    public MyClass() { }
}

4.3 泛型约束的优势

  • 类型安全:确保泛型参数满足特定的条件,避免运行时错误。
  • 灵活性:可以根据需要添加多个约束,进一步限制泛型参数的类型。

5. 实践练习

5.1 练习1:实现一个泛型栈

实现一个泛型栈(Stack)类,支持压入(Push)、弹出(Pop)和查看栈顶元素(Peek)操作。

public class GenericStack<T>
{
    private List<T> _items = new List<T>();

    public void Push(T item)
    {
        _items.Add(item);
    }

    public T Pop()
    {
        if (_items.Count == 0)
        {
            throw new InvalidOperationException("Stack is empty");
        }
        T item = _items[_items.Count - 1];
        _items.RemoveAt(_items.Count - 1);
        return item;
    }

    public T Peek()
    {
        if (_items.Count == 0)
        {
            throw new InvalidOperationException("Stack is empty");
        }
        return _items[_items.Count - 1];
    }

    public int Count => _items.Count;
}

5.2 练习2:使用泛型方法实现排序

实现一个泛型方法,对数组进行排序。可以使用Array.Sort方法。

public class GenericSortExample
{
    public static void Sort<T>(T[] array) where T : IComparable<T>
    {
        Array.Sort(array);
    }
}

class Program
{
    static void Main()
    {
        int[] numbers = { 3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5 };
        GenericSortExample.Sort(numbers);
        Console.WriteLine(string.Join(", ", numbers)); // 输出: 1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9

        string[] words = { "banana", "apple", "cherry", "date" };
        GenericSortExample.Sort(words);
        Console.WriteLine(string.Join(", ", words)); // 输出: apple, banana, cherry, date
    }
}

6. 总结

泛型是C#中一个非常重要的特性,它允许你编写灵活、类型安全的代码。通过泛型类、泛型方法和泛型约束,你可以大大提高代码的重用性和可维护性。

6.1 关键点回顾

  • 泛型类:使用泛型参数定义类,提高代码重用性和类型安全性。
  • 泛型方法:在方法级别使用泛型参数,增加代码的灵活性。
  • 泛型约束:限制泛型参数的类型,确保类型安全。

6.2 下一步学习

  • 委托和事件:学习如何使用委托和事件处理程序。
  • LINQ:掌握语言集成查询(LINQ),用于数据查询和操作。
  • 异步编程:了解如何使用async和await进行异步编程。

通过不断练习和实践,你将能够熟练掌握泛型,并将其应用于实际项目中。